use crossterm::event::{Event, KeyCode, MouseButton, MouseEventKind::*};

use blk1010_lib::{block::*, fill::{BlockFillMaster, CalcState}, map::*};
use log::info;
use std::any::Any;
use std::time::Instant;
use tge::{
    context::Context,
    event::{event_check, event_emit, timer_fire, timer_register},
    game::Model,
    util::{Point, Rand},
};

pub const BLK1010W: usize = 32;
pub const BLK1010H: usize = 16;

pub enum Blk1010State {
    Normal,
    Gameover,
}

//标记区域
pub enum Blk1010Area {
    MINIBLK1,
    MINIBLK2,
    MINIBLK3,
}

pub struct Blk1010Model {
    pub rand: Rand,
    pub map: Map,
    pub mini_blks: Vec<u8>,
    pub mini_flow: u8,
    pub flow_src: u8,
    pub flow_pos: Point,
    pub flow_pos_near: Point,
    pub last_pos: Point,
    pub fillman: BlockFillMaster,
}

impl Blk1010Model {
    pub fn new() -> Self {
        Self {
            rand: Rand::new(),
            map: Map::new(8, 8),
            mini_blks: vec![],
            mini_flow: 255,
            flow_src: 255,
            flow_pos: Point { x: 0, y: 0 },
            flow_pos_near: Point { x: 255, y: 255 },
            last_pos: Point { x: 0, y: 0 },

            fillman: BlockFillMaster::new(10),
        }
    }

    pub fn mouse_in(&self, x: u16, y: u16) -> Option<Blk1010Area> {
        if x >= 1 && x <= 10 && y >= 18 && y <= 27 {
            return Some(Blk1010Area::MINIBLK1);
        }
        if x >= 12 && x <= 21 && y >= 18 && y <= 27 {
            return Some(Blk1010Area::MINIBLK2);
        }
        if x >= 23 && x <= 32 && y >= 18 && y <= 27 {
            return Some(Blk1010Area::MINIBLK3);
        }
        None
    }

    pub fn flow(&mut self, idx: usize, x: u16, y: u16) {
        if self.mini_blks[idx] != 255 {
            if self.mini_flow != 255 {
                self.mini_blks[self.flow_src as usize] = self.mini_flow;
            }
            self.mini_flow = self.mini_blks[idx];
            self.mini_blks[idx] = 255;
            self.flow_src = idx as u8;
            self.flow_pos.x = 0;
            self.flow_pos.y = 0;
            self.last_pos.x = x;
            self.last_pos.y = y;
            self.flow_pos_near.x = 0;
            self.flow_pos_near.y = 0;
            //info!("mouse down...idx={:?}, flow={:?}", idx, self.mini_flow);
            event_emit("Blk1010.RedrawMini");
            event_emit("Blk1010.RedrawFlow");
        }
    }

    pub fn flow_set_near(&mut self) {
        let fb = self.mini_flow;
        let fx = self.flow_pos.x / 4;
        let fy = self.flow_pos.y / 2;

        match self.map.put_down(fb, fx as u8, fy as u8, true) {
            Ok(cp) => {
                if cp {
                    self.flow_pos_near.x = fx;
                    self.flow_pos_near.y = fy;
                } else {
                    self.flow_pos_near.x = 255;
                    self.flow_pos_near.y = 255;
                }
            }
            Err(_) => {}
        }
    }

    pub fn check_dead(&mut self) -> bool {
        let start = Instant::now();
        let mut m2 = 0;
        for i in 0..3 {
            let mb = self.mini_blks[i];
            if mb == 255 {
                m2 += 1;
                continue;
            }
            let rc = self.map.row - BLOCKS[mb as usize - 1].rect.height as u8 + 1;
            let cc = self.map.col - BLOCKS[mb as usize - 1].rect.width as u8 + 1;

            for m in 0..rc {
                for n in 0..cc {
                    if self.map.put_down(mb, n, m, true).unwrap() {
                        let duration = start.elapsed();
                        info!("check dead time...{:?}", duration);
                        return false;
                    }
                }
            }
        }
        let duration = start.elapsed();
        info!("check dead time...{:?}", duration);
        m2 != 3
    }

    pub fn reset(&mut self, context: &mut Context) {
        context.input_events.clear();
        context.state = Blk1010State::Normal as u8;
        self.map.set_grid_byid(0);
        self.rand.srand_now();
        self.mini_blks = vec![255,255,255];
        self.fillman.fill_request(&mut self.map);
        info!("blks...{:?}", self.mini_blks);
        //Test put down...
        // self.map.put_down(50, 4, 1, false).unwrap();

        self.map.hard_type = 1; // 测试，开启按分数增加难度模式
        self.map.hard_level = -1; // 测试加4个难度的偏移量
        self.fillman.set_score_diff(vec![180i32,30000i32,60000i32,90000i32]);

        self.map.combo = 0;
        self.map.score = 0;
        self.map.collection = 0;
        event_emit("Blk1010.RedrawMini");
        timer_register("Blk1010.Clear", 0.6, "_");
    }
}

impl Model for Blk1010Model {
    fn init(&mut self, context: &mut Context) {
        self.reset(context);
    }

    fn handle_input(&mut self, context: &mut Context, _dt: f32) {
        let es = context.input_events.clone();
        for e in &es {
            match e {
                Event::Key(key) => match key.code {
                    KeyCode::Char('r') => {
                        self.reset(context);
                    }
                    _ => {}
                },
                Event::Mouse(mou) => {
                    //info!("{:?}", mou);
                    if mou.kind == Up(MouseButton::Left) {
                        if self.flow_pos_near.x == 255 {
                            if self.mini_flow != 255 {
                                self.mini_blks[self.flow_src as usize] = self.mini_flow;
                                self.mini_flow = 255;
                                event_emit("Blk1010.RedrawFlow");
                                event_emit("Blk1010.RedrawMini");
                            }
                        } else {
                            let _cp = self
                                .map
                                .put_down(
                                    self.mini_flow,
                                    self.flow_pos_near.x as u8,
                                    self.flow_pos_near.y as u8,
                                    false,
                                )
                                .unwrap();
                            self.map.score += 10;
                            self.map.calc_full();
                            if self.map.full_rows.len() != 0 || self.map.full_cols.len() != 0 {
                                self.map.combo += 1;
                                self.map.collection += 1; //模拟收集物
                                self.map.score += self.map.combo as u32 * 10;
                                self.map.score += self.map.full_rows.len() as u32 * 10;
                                self.map.score += self.map.full_cols.len() as u32 * 10;
                                if self.map.score > self.map.clear_all_score + 200 {
                                    self.map.clear_all_score = 0;
                                }
                                timer_fire("Blk1010.Clear", 0);
                            } else {
                                self.map.combo = 0;
                                if self.check_dead() {
                                    context.state = Blk1010State::Gameover as u8;
                                }
                            }
                            self.mini_flow = 255;
                            self.flow_pos_near.x = 255;
                            self.flow_pos_near.y = 255;
                            if self.mini_blks[0] == 255
                                && self.mini_blks[1] == 255
                                && self.mini_blks[2] == 255
                            {
                                self.fillman.fill_request(&mut self.map);
                            }
                            event_emit("Blk1010.RedrawFlow");
                            event_emit("Blk1010.RedrawMini");
                        }
                    }
                    if mou.kind == Drag(MouseButton::Left) {
                        if self.mini_flow != 255 {
                            let xx = self
                                .flow_pos
                                .x
                                .saturating_add_signed(mou.column as i16 - self.last_pos.x as i16);
                            let yy = self
                                .flow_pos
                                .y
                                .saturating_add_signed(mou.row as i16 - self.last_pos.y as i16);
                            let mut update = false;
                            if xx < 32 {
                                self.flow_pos.x = xx;
                                self.last_pos.x = mou.column;
                                update = true;
                            }
                            if yy < 16 {
                                self.flow_pos.y = yy;
                                self.last_pos.y = mou.row;
                                update = true;
                            }
                            if update {
                                self.flow_set_near();
                                event_emit("Blk1010.RedrawFlow");
                                event_emit("Blk1010.RedrawMini");
                            }
                        }
                    }
                    if mou.kind == Down(MouseButton::Left) {
                        match self.mouse_in(mou.column, mou.row) {
                            Some(Blk1010Area::MINIBLK1) => {
                                self.flow(0, mou.column, mou.row);
                            }
                            Some(Blk1010Area::MINIBLK2) => {
                                self.flow(1, mou.column, mou.row);
                            }
                            Some(Blk1010Area::MINIBLK3) => {
                                self.flow(2, mou.column, mou.row);
                            }
                            _ => {}
                        }
                    }
                }
                _ => {}
            }
        }
        context.input_events.clear();
    }

    fn handle_auto(&mut self, _context: &mut Context, _dt: f32) {
        self.fillman.update();
        if self.fillman.cstate == CalcState::FillOk {
            self.mini_blks.clear();
            info!("@@@@@@@@RET....{:?}", self.fillman.ret);
            for r in &self.fillman.ret {
                self.mini_blks.push(*r);
            }
            event_emit("Blk1010.RedrawMini");
            self.fillman.cstate = CalcState::Idle;
        }
    }

    fn handle_event(&mut self, _context: &mut Context, _dt: f32) {}

    fn handle_timer(&mut self, context: &mut Context, _dt: f32) {
        if event_check("Blk1010.Clear", "_") {
            self.map.clear_full();
            if self.check_dead() {
                context.state = Blk1010State::Gameover as u8;
            }
            event_emit("Blk1010.RedrawFlow");
        }
    }

    fn as_any(&mut self) -> &mut dyn Any {
        self
    }
}
